package com.googlecode.marrowboy;

import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;

import com.googlecode.marrowboy.assertionresult.AssertionDetail;

/**
 * Provides methods which can be used in place of the Hamcrest methods, and
 * methods to configure the behaviour of MarrowBoy reports.
 * 
 * This class contains the only methods which are expected to be called by
 * users. It is expected to be used statically so instantiation is forbidden.
 * 
 * @author mjg
 */
public final class MarrowBoyAssert {

    private static final String UNSPECIFIED_REASON = "";

    /**
     * No instantiation.
     */
    private MarrowBoyAssert() {
    }

    /**
     * Provides the same functionality as
     * {@link MatcherAssert#assertThat(Object, Matcher)}, except that it also
     * adds the assertion to the generated report.
     * 
     * @param actual
     *            the object produced by the system under test.
     * @param matcher
     *            the {@link Matcher} which will be applied to the first
     *            argument.
     * @return the {@link AssertionDetail} which was generated for this
     *         assertion.
     */
    public static <T> void assertThat(final T actual, final Matcher<? super T> matcher) {
        realAssertThat(UNSPECIFIED_REASON, actual, matcher);
    }

    /**
     * Provides the same functionality as
     * {@link MatcherAssert#assertThat(String, Object, Matcher)}, except that it
     * also adds the assertion to the generated report.
     * 
     * @param reason
     *            some textual description of the assertion.
     * @param actual
     *            the object produced by the system under test.
     * @param matcher
     *            the {@link Matcher} which will be applied to the first
     *            argument.
     * @return the {@link AssertionDetail} which was generated for this
     *         assertion.
     */
    public static <T> void assertThat(final String reason, final T actual, final Matcher<? super T> matcher) {
        realAssertThat(reason, actual, matcher);
    }
    
    private static <T> void realAssertThat(final String reason, final T actual, final Matcher<? super T> matcher) {

        StackTraceElement testStackFrame = extractStackTraceElementOfOriginalTest();
        String className = testStackFrame.getClassName();
        String methodName = testStackFrame.getMethodName();

        try {
            FakeMarrowBoyAssert.assertThat(actual, matcher);

        } catch (final AssertionError ae) {
            MarrowBoy.getTestLogger().logAssertion(className, methodName, reason, actual, matcher, false);
            throw ae;

        }

        MarrowBoy.getTestLogger().logAssertion(className, methodName, reason, actual, matcher, true);

    }

    public static StackTraceElement extractStackTraceElementOfOriginalTest() {
        // if we pop this many elements off the call stack, we'll be looking at
        // the method which first called us.
        final int stackPopAmount = 4;

        StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[stackPopAmount];

        // This happens if we're using the java agent, because of an extra
        // bytecode-generated proxy call
        // TODO: Do this better...
        if ("org.hamcrest.MatcherAssert".equals(stackTraceElement.getClassName())) {
            stackTraceElement = Thread.currentThread().getStackTrace()[stackPopAmount + 1];
        }
        return stackTraceElement;
    }

}
